#include "threadPool.h"

int main(int argc, char *argv[])
{
    printf("CMD_LS = %d\n",CMD_LS);
    
    printf("CMD_LOGIN_APPLY = %d\n",CMD_LOGIN_APPLY);
    drawBanner();
    char time_now[50];
    bzero(&time_now,sizeof(time_now));
    time_t now = time(NULL);
    ctime_r(&now,time_now);
    printf("now = %s\n",time_now);
    //1.初始化数据库
    dataBase_t dataBase;
    snprintf(dataBase.host,sizeof(dataBase.host),"localhost");
    snprintf(dataBase.user,sizeof(dataBase.user),"root");
    snprintf(dataBase.passwd,sizeof(dataBase.passwd),"123");
    snprintf(dataBase.db,sizeof(dataBase.db),"deepsleep");
    initGlobalDataBase(dataBase);
    //2.初始化线程池,并创建工人
    threadPool_t threadPool;
    int workerNum = atoi(argv[3]);
    threadPoolInit(&threadPool,workerNum);
    makeWorker(&threadPool);
    //3.TCP监听
    int sockfd = tcpInit(argv[1],argv[2]);
    printf("等待客户端连接！\n");

    //4.创建epoll实例
    int epfd = epoll_create(1);
    epollAdd(epfd,sockfd);
    struct epoll_event readyset[1024];

    //4.1定义并初始化超时踢人相关的数据结构
    hash_table_t *fd_hash = hash_init(128);     //记录连接fd上一次活跃的时间
    FdSet fdQueue[QUEUE_SIZE];                   //用于实现超时踢人的长为30的集合队列

    for (int i = 0; i < QUEUE_SIZE; ++i) {
        fdset_init(&fdQueue[i], MAX_FD);
    }

    for(int i =0;i<MAX_FD;i++){
        fdQueue[i].count = 0;
    }

    // int timer_fd = timerfd_create(CLOCK_MONOTONIC, 0);
    // struct itimerspec timer_spec;
    // timer_spec.it_interval.tv_sec = 1;  // 每1秒
    // timer_spec.it_interval.tv_nsec = 0;
    // timer_spec.it_value.tv_sec = 1;     // 首次1秒后触发
    // timer_spec.it_value.tv_nsec = 0;
    // timerfd_settime(timer_fd, 0, &timer_spec, NULL);

    // // 加入epoll
    // epollAdd(epfd, timer_fd);

    // int current_index = 0;
    while(1){
        int readynum = epoll_wait(epfd,readyset,1024,1000);
        now = time(NULL);
        ctime_r(&now,time_now);
        // printf("now = %s\n",time_now);
        //4.2处理当前时间槽
        int current_slot = now % QUEUE_SIZE;
        FdSet *current_set = &fdQueue[current_slot];
        //踢出当前时间槽中所有连接
        // printf("current_slot= %d\n",current_slot);
        // printf("current_set->count= %d\n",current_set->count);
        // if(current_set->count >0){
        //     printf("时间槽%d号里已有连接,踢出%d个连接\n",current_slot,current_set->count);  
        //     for(int j =0;j<current_set->count;j++){
        //         int fdtokick = current_set->fds[j];
        //         epollDel(epfd,fdtokick);
        //         close(fdtokick);
        //         hash_remove(fd_hash, fdtokick);
        //         printf("从时间槽%d号里踢出fd = %d的连接\n",current_slot,fdtokick);
        //     }
        //     current_set->count =0;  //清空集合
        // }

        for(int i = 0; i < readynum; ++i){
            //5.处理新连接
            //todo：计算token ，并发送给客户端
            if(readyset[i].data.fd == sockfd){
				int netfd = accept(sockfd,NULL,NULL);  
                if(netfd == -1) {
                    perror("accept");
                    continue;
                }
                printf("主线程收到了连接,新的netfd = %d\n",netfd);
				epollAdd(epfd,netfd);   //接受新连接，并加入监听
                //设置非阻塞模式
                int flags = fcntl(netfd, F_GETFL, 0);
                fcntl(netfd, F_SETFL, flags | O_NONBLOCK);  
                //将新连接添加到当前时间槽
                fdset_add(current_set, netfd);
                hash_upsert(fd_hash, netfd, current_slot);
                printf("添加新fd = %d 到 %d 号时间槽\n",netfd,current_slot);
            }
			else { 
                printf("客户端来请求了\n");
                //6.初始化传入传出参数
                protocol_header_t header;
                bzero(&header,sizeof(header));
				char buf[4096];
                size_t buf_len = 4096;
				bzero(buf,sizeof(buf));
                //7.接收数据
				int netfd = readyset[i].data.fd;
                verify_token_auth(sockfd,&header);
				int ret = recvCommand(netfd,&header,buf,buf_len);
                //int recvCommand(int sockfd, protocol_header_t *header, char *buf, size_t buf_size)
                printf("epollin first revcCommand ret = %d\n",ret);
                //8.连接关闭或出错
                if(ret < 0) { 
                    perror("recvCommand error");
                    printf("接受失败，或连接关闭\n");
                    //关闭socket
                    close(netfd);
        
                    //从epoll中移除
                    epollDel(epfd,netfd);
                    //从fdset和hashset中移除
                    int slot_index = hash_get(fd_hash, netfd);
                    if(slot_index != -1){
                        fdset_remove(&fdQueue[slot_index], netfd);
                    }
                    hash_remove(fd_hash, netfd);
                    continue;
                }

                printf("main get netfd,netfd = %d\n,cmd = %d,buf = %s\n",netfd,header.command,buf);
				printf("recvCommand ret = %d\n",ret);               // 关键修改：处理连接关闭情况 
                //9.长短命令分离,根据数据类型，选择主线程处理，或者子线程上传下载
                if(header.command == CMD_GETS || header.command == CMD_PUTS){
				    pthread_mutex_lock(&threadPool.mutex);
				    enQueue(&threadPool.taskQueueLong,netfd,&header,buf);
				    pthread_cond_broadcast(&threadPool.condLong);
				    pthread_mutex_unlock(&threadPool.mutex);                    
                }else{
                    //如果fd曾就绪，就移除它
                    int prev_slot = hash_get(fd_hash, netfd);
                    if(prev_slot != -1 &&prev_slot != current_slot){
                        fdset_remove(&fdQueue[prev_slot], netfd);
                    }
                    //增加到当前集合
                    fdset_add(current_set, netfd);
                    hash_upsert(fd_hash, netfd, current_slot);

				    pthread_mutex_lock(&threadPool.mutex);
                    //短命令的连接是不关的,而且不需要token，无脑执行
                    processShortCommand(netfd, header.command, buf);
				    pthread_mutex_unlock(&threadPool.mutex);
                }
			}
        }
    }
    // 清理资源（实际可能不会执行到这里,服务端永远不关）
    close(sockfd);
    close(epfd);
    return 0;
}


